package com.user.mediacoderdemo;

import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.graphics.ImageFormat;
import android.graphics.YuvImage;
import android.media.Image;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.logging.Logger;



/**
 * Created by zoudong on 2017/2/7.
 * 用户视频和音频的合并  和分割
 */

public class MediaOperate {
    private static final long TIMEOUT_US = -1;
    public static final String TAG = "MediaOperate";
    /**
     * 1. new MediaExtractor  对象  导入数据源
     * 2. 找到 需要处理的 视频或者音频  track
     * 3. MediaMuxer.addTrack  添加轨道
     * 4. extractor.readSampleData(audiobuffer, 0);
     * 5. mediaMuxer.writeSampleData(extractse.trackindex, audiobuffer, info);
     *
     * @param data
     * @throws IOException
     */
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    @SuppressWarnings("ResourceType")
    public static void operateMedia(MediaOperateData data) throws IOException {
        if (data == null || TextUtils.isEmpty(data.output)) {
            return;
        }
        Log.e("zoudong", "-----" + data.toString());
        long duration = data.duration;
        MediaMuxer mediaMuxer = new MediaMuxer(data.output, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
        for (MediaOperateData.MediaExtracts extractse : data.mediaExtractses) {
            MediaFormat mediaFormat = VideoInfoKit.getMediaFormat(extractse.tag, extractse.extractor, true);
            if (extractse.start_duration < 0 || extractse.start_duration >= extractse.end_duration) {
                throw new IllegalArgumentException("start_duration or  end_duration  is Invalid");
            }
            if (mediaFormat == null) {
                throw new IllegalArgumentException("format must not be null."); //  java.lang.IllegalArgumentException: format must not be null.
            }
            extractse.trackindex = mediaMuxer.addTrack(mediaFormat);
        }
        mediaMuxer.start();
        for (MediaOperateData.MediaExtracts extractse : data.mediaExtractses) {
            // 封装音频track   Unsupported mime 'audio/mpeg'   不支持 mp3
            Log.d("zoudong", "-----" + extractse.toString());
            MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
            info.presentationTimeUs = 0;
            MediaExtractor extractor = extractse.extractor;
            if (extractse.start_duration > 0) {
                extractor.seekTo(extractse.start_duration, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
            }
            int trackIndex = extractor.getSampleTrackIndex();
            ByteBuffer audiobuffer = ByteBuffer.allocate(extractor.getTrackFormat(trackIndex).getInteger(MediaFormat.KEY_MAX_INPUT_SIZE));
            while (true) {
                int sampleSize = extractor.readSampleData(audiobuffer, 0);
                if (sampleSize < 0) {
                    //这里一定要释放选择的轨道，不然另一个轨道就无法选中了
                    extractor.unselectTrack(trackIndex);
                    break;
                }
                //获取时间戳
                long presentationTimeUs = extractor.getSampleTime();
                //获取帧类型，只能识别是否为I帧
                int sampleFlag = extractor.getSampleFlags();
                extractor.advance();
                info.offset = 0;
                info.size = sampleSize;
                info.flags = sampleFlag;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                    //  java.lang.IllegalArgumentException: bufferInfo must specify a valid buffer offset, size and presentation time
                    mediaMuxer.writeSampleData(extractse.trackindex, audiobuffer, info);
                }
                info.presentationTimeUs = extractor.getSampleTime() - extractse.start_duration;  // seek 开始时间  需要减去
                long dur = duration > 0 ? duration : extractse.end_duration;
                if (dur > 0 && info.presentationTimeUs > dur) {//时间
                    break;
                }
            }
//            extractor.release();
        }
        mediaMuxer.stop();
        mediaMuxer.release();
        mediaMuxer = null;
    }

    public static void release(MediaOperateData data) {
        for (MediaOperateData.MediaExtracts extractse : data.mediaExtractses) {
            extractse.extractor.release();
            extractse.extractor = null;
        }
    }

    //    private static final String MIME_TYPE = MediaCodec.MIMETYPE_VIDEO_MPEG2;    // H.264 Advanced Video Coding
    private static final int FRAME_RATE = 15;               // 15fps
    private static final int IFRAME_INTERVAL = 10;          // 10 seconds between I-frames
    private static final int NUM_FRAMES = 30;               // two seconds of video

    public static void MediaCodec(MediaExtractor extractor, MediaFormat format) throws IOException {
        String string = format.getString(MediaFormat.KEY_MIME);
        Log.e("zoudong", "MediaFormat==" + format.toString());
//         int count =MediaCodecList.getCodecCount();
//        for (int i = 0; i < count; i++) {
//            MediaCodecInfo codecInfoAt = MediaCodecList.getCodecInfoAt(i);
//            MediaCodecInfo.CodecCapabilities capabilitiesForType = codecInfoAt.getCapabilitiesForType(format.getString(MediaFormat.KEY_MIME));
//        }
        format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 0);
        int mWidth = 320;
        int mHeight = 240;
        int mBitRate = 2000000;
        MediaFormat format1 = MediaFormat.createVideoFormat(string, mWidth, mHeight);

        format1.setInteger(MediaFormat.KEY_COLOR_FORMAT,
                MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
        format1.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate);
        format1.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
        format1.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
        Log.e("zoudong", "MediaFormat==" + format1.toString());
        MediaCodec codec = MediaCodec.createEncoderByType(string);
        codec.configure(format1, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);//配置
        codec.start();
        ByteBuffer[] inputBuffers = codec.getInputBuffers();
        ByteBuffer[] outputBuffers = codec.getOutputBuffers();
        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
        long startMs = System.currentTimeMillis();
        boolean isEOS = false;
        MediaMuxer mediaMuxer = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            mediaMuxer = new MediaMuxer("/storage/emulated/0/output.mp4", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
        }
        int mTrackIndex = -1;
        boolean mMuxerStarted = false;
        while (true) {
            //读数据
            if (!isEOS) {
                int inIndex = codec.dequeueInputBuffer(-1);  // timeout   -1 时一直等待
                if (inIndex >= 0) {
                    int sampleSize = extractor.readSampleData(inputBuffers[inIndex], 0);
                    if (sampleSize > 0) {
                        codec.queueInputBuffer(inIndex, 0, sampleSize, extractor.getSampleTime(), 0);
                        extractor.advance();
                    } else {
                        //结束了 EOS
                        isEOS = true;
                        codec.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                    }
                }
            }

            // 写数据
            int outIndex = codec.dequeueOutputBuffer(info, -1);
            Log.e("zoudong", "outIndex=" + outIndex);
            if (outIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                Log.d("zoudong", "INFO_OUTPUT_BUFFERS_CHANGED");
                outputBuffers = codec.getOutputBuffers();
            } else if (outIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                Log.d("zoudong", "New format " + codec.getOutputFormat());
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                    mTrackIndex = mediaMuxer.addTrack(codec.getOutputFormat());
                    mediaMuxer.start();
                }
                mMuxerStarted = true;

            } else if (outIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
                Log.d("zoudong", "dequeueOutputBuffer timed out!");
            } else if (outIndex < 0) {

            } else {
                Log.e("zoudong", "outIndex=" + outIndex);
                ByteBuffer buffer = outputBuffers[outIndex];
                Log.d("zoudong", "We can't use this buffer but render it due to the API limit, " + buffer);
                if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
                    // The codec config data was pulled out and fed to the muxer when we got
                    // the INFO_OUTPUT_FORMAT_CHANGED status.  Ignore it.
                    Log.d("zoudong", "ignoring BUFFER_FLAG_CODEC_CONFIG");
//                        info.size = 0;
                } else {
                    if (info.size != 0) {
                        if (!mMuxerStarted) {
                            throw new RuntimeException("muxer hasn't started");
                        }

                        // adjust the ByteBuffer values to match BufferInfo (not needed?)
                        buffer.position(info.offset);
                        buffer.limit(info.offset + info.size);
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                            mediaMuxer.writeSampleData(mTrackIndex, buffer, info);
                        }
                        Log.d("zoudong", "sent " + info.size + " bytes to muxer");
                    }
                    codec.releaseOutputBuffer(outIndex, false);
                }

            }
            // All decoded frames have been rendered, we can stop playing now
            if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                Log.d("zoudong", "OutputBuffer BUFFER_FLAG_END_OF_STREAM");
                break;
            }
        }
        codec.stop();
        codec.release();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            mediaMuxer.stop();
            mediaMuxer.release();
        }

    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public static void MeidaFrameToImage(MediaCodec decoder, MediaExtractor extractor, MediaFormat format) throws IOException {
        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
        boolean sawInputEOS = false;
        boolean sawOutputEOS = false;
        decoder.configure(format, null, null, 0); //配置
        decoder.start();
        int cout = 0;
        while (!sawOutputEOS) {
            //1.读取数据
            if (!sawInputEOS) {
                int inputBufferId = decoder.dequeueInputBuffer(TIMEOUT_US);
                if (inputBufferId >= 0) {
                    ByteBuffer inputBuffer = decoder.getInputBuffer(inputBufferId);
                    Log.e(TAG, "decoder.getInputBuffer(inputBufferId)");
                    int sampleSize = -1;
                    if (inputBuffer != null) {
                        sampleSize = extractor.readSampleData(inputBuffer, 0);
                        Log.e(TAG, "extractor.readSampleData(inputBuffer, 0)");
                        if (sampleSize > 0) {
                            long presentationTimeUs = extractor.getSampleTime();
                            decoder.queueInputBuffer(inputBufferId, 0, sampleSize, presentationTimeUs, 0);
                            extractor.advance();
                            Log.e(TAG, "extractor.advance()");
                        } else {
                            decoder.queueInputBuffer(inputBufferId, 0, 0, 0L, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                            sawInputEOS = true;
                            Log.e(TAG, "sawInputEOS =true");
                        }
                    } else {
                        throw new RuntimeException("getInputBuffer return null");
                    }
                } else {
                    throw new RuntimeException("dequeueInputBuffer return " + inputBufferId);
                }
            }
            //2.解析数据
            ByteBuffer outputBuffer = null;

            int outputBufferId = decoder.dequeueOutputBuffer(info, TIMEOUT_US);
            Log.e("zoudong", "MeidaFrameToImage: "+outputBufferId );
            if (outputBufferId == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                Log.d("zoudong", "INFO_OUTPUT_BUFFERS_CHANGED");
            } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                Log.d("zoudong", "New format " + decoder.getOutputFormat());
//                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
//                    mTrackIndex = mediaMuxer.addTrack(decoder.getOutputFormat());
//                    mediaMuxer.start();
//                }
//                mMuxerStarted = true;

            } else if (outputBufferId == MediaCodec.INFO_TRY_AGAIN_LATER) {
                Log.d("zoudong", "dequeueOutputBuffer timed out!");
            } else if (outputBufferId < 0) {

            } else {
                if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                    // The codec config data was pulled out and fed to the muxer when we got
                    // the INFO_OUTPUT_FORMAT_CHANGED status.  Ignore it.
                    Log.e("zoudong", "MeidaFrameToImage: sawOutputEOS=true");
                    sawOutputEOS = true;
                }
                outputBuffer = decoder.getOutputBuffer(outputBufferId);
                if (info.size != 0) {
                    Log.e(TAG, "MeidaFrameToImage: " + (outputBuffer == null));
                    if (outputBuffer != null && outputBuffer.hasRemaining()) {
                        File file = new File("/storage/emulated/0/image" + cout + ".jpg");
                        RandomAccessFile randomAccessFile = new RandomAccessFile("/storage/emulated/0/image" + cout + ".jpg", "rwd");
                        FileChannel   fc = randomAccessFile.getChannel();
                        fc.write(outputBuffer);
                        fc.close();
                    }
                    cout++;
                }
                decoder.releaseOutputBuffer(outputBufferId, info.size != 0);
            }
        }
    }
//            if (outputBufferId >= 0) {
//                if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
//                    sawOutputEOS = true;
//                }
//                Logger.LogD(TAG,"decoder.dequeueOutputBuffer(info, DEFAULT_TIMEOUT_US)");
//                boolean doRender = (info.size != 0);
//                if (doRender) {
//                    outputFrameCount++;
//                    if (callback != null) {
//                        callback.onDecodeFrame(outputFrameCount);
//                    }
//                    Image image = decoder.getOutputImage(outputBufferId);
//                    Logger.LogD(TAG,"image format: " + image.getFormat());
//                    ByteBuffer buffer = null;
//                    if(image!=null&&image.getPlanes()!=null){
//                        buffer = image.getPlanes()[0].getBuffer();
//                        byte[] arr = new byte[buffer.remaining()];
//                        buffer.get(arr);
//                        if (mQueue != null) {
//                            try {
//                                mQueue.put(arr);
//                            } catch (InterruptedException e) {
//                                e.printStackTrace();
//                            }
//                        }
//
//                        if (outputImageFormat != null) {
//                            String fileName;
//                            switch (outputImageFormat) {
//                                case I420:
//                                    fileName = OUTPUT_DIR + String.format("frame_%05d_I420_%dx%d.yuv", outputFrameCount, width, height);
//                                    dumpFile(fileName, getDataFromImage(image, COLOR_FormatI420));
//                                    break;
//                                case NV21:
//                                    fileName = OUTPUT_DIR + String.format("frame_%05d_NV21_%dx%d.yuv", outputFrameCount, width, height);
//                                    dumpFile(fileName, getDataFromImage(image, COLOR_FormatNV21));
//                                    break;
//                                case JPEG:
//                                    fileName = OUTPUT_DIR + System.currentTimeMillis();
//                                    compressToJpeg(fileName, image);
//                                    Logger.LogD(TAG,"compressToJpeg");
//                                    break;
//                            }
//                        }
//                        image.close();
//
//                        decoder.releaseOutputBuffer(outputBufferId, true);

}
